AWS CDK v2でECRのレポジトリを作ってコンテナイメージをpushし、Lambdaで実行してみた
データアナリティクス事業本部の鈴木です。
Rancher Desktopをインストールしたローカル環境からAmazon ECRにコンテナイメージを準備して、AWS Lambdaで実行してみたので、試した内容をまとめてみました。
検証の内容
今回は簡単に、以下の3点を確認しました。
- AWS CDKでECRにレポジトリを作成する。
- dockerコマンドでECRのレポジトリにコンテナイメージをpushする。
- コンテナをLambdaで実行する。
ECRのレポジトリにコンテナイメージを上げておくと、Lambdaはもちろん、ECSやSageMakerなどのさまざまなサービスによりコンテナを利用することができます。
今回はその例の一つとして、簡単ではありますが、レポジトリの作成からLambdaでの実行までを試してみました。
環境
以下の環境で検証しました。
- Rancher Desktop:
Version: 1.8.1
- AWS CDK:
2.73.0
特にRancher Desktopについては、以下の記事ような手順であらかじめインストールしておきました。
やってみる
1. ECRレポジトリの作成
まず、以下のようにプロジェクトを作成しました。
mkdir cdk_erc cd cdk_erc cdk init sample-app --language typescript
lib/cdk_erc-stack.ts
は以下のようにしました。
import * as cdk from 'aws-cdk-lib'; import * as ecr from "aws-cdk-lib/aws-ecr"; import { Construct } from 'constructs'; export class CdkErcStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // ECRレポジトリ const lambdaEcrRepository = new ecr.Repository(this, 'Repository', { repositoryName: "cm-nayuts-sample-repo", removalPolicy: cdk.RemovalPolicy.DESTROY, autoDeleteImages: true }); } }
今回はレポジトリを削除しやすいように、removalPolicy
とautoDeleteImages
に値を設定してみました。ただし、運用する際は間違って消えてしまう可能性もあるので注意が必要です。
cdk deploy
でデプロイします。私の環境ではconfigファイルで指定したプロファイルでAWS環境にアクセスするようにしているので、--profile
オプションを付けました。
cdk deploy --profile プロファイル名
--profile
オプションについては、AWS CDK Toolkit (cdk command) - AWS Cloud Development Kit (AWS CDK) v2のSpecifying Region and other configurationをご確認ください。
以下のように作成されました。
リポジトリタグも設定されています。
2. コンテナイメージのpush
プロジェクトのルートにdocker
ディレクトリとsrc
ディレクトリを作成しました。
以下のようになるよう、ファイルを配置しました。
cd docker tree # docker # ├── docker_build.sh # └── dockerfile cd lambda tree # src # ├── app.py # └── requirements.txt
以下は作成したファイルの説明です。
なお、app.py
およびdockerfile
は、2023/4/9時点でAWS Lambdaデベロッパーガイドのコンテナイメージで Python Lambda 関数をデプロイするに記載の内容を使用させて頂きました。
docker_build.sh
これはdockerfileからコンテナイメージをビルドしてECRにpushするツールです。
#!/bin/bash ACCOUNTID=${1} REGION=${2} ECR_REPO_NAME=${3} AWS_PROFILE=${4} ECR_REPO_URI="${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com/${ECR_REPO_NAME}" echo "ECR_REPO_URI: ${ECR_REPO_URI}" aws ecr get-login-password --region ${REGION} --profile ${AWS_PROFILE} | docker login --username AWS --password-stdin ${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com docker build -f ./docker/dockerfile -t ${ECR_REPO_NAME} . docker tag ${ECR_REPO_NAME}:latest ${ECR_REPO_URI}:latest docker push ${ECR_REPO_URI}:latest
dockerfile
デベロッパーガイド記載の、Python関数のコンテナイメージ作成用のdockerfileです。
FROM public.ecr.aws/lambda/python:3.8 # Install the function's dependencies using file requirements.txt # from your project folder. COPY ./src/requirements.txt . RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" # Copy function code COPY ./src/app.py ${LAMBDA_TASK_ROOT} # Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile) CMD [ "app.handler" ]
app.py
デベロッパーガイド記載の、Python関数で実行するスクリプトです。
import sys def handler(event, context): return 'Hello from AWS Lambda using Python' + sys.version + '!'
requirements.txt
コンテナイメージのビルド時にインストールするPythonライブラリの一覧です。今回は特に必要がないので空ファイルにしておきました。
ここまでで、プロジェクトは以下のようなディレクトリ構成になっています。
tree -L 1 # . # ├── README.md # ├── bin # ├── cdk.json # ├── docker # ├── jest.config.js # ├── lib # ├── node_modules # ├── package-lock.json # ├── package.json # ├── src # ├── test # └── tsconfig.json
プロジェクトのルートから、以下のようにツールを実行して、コンテナイメージをECRのレポジトリにpushしました。
./docker_build.sh 自分のAWSアカウントID ap-northeast-1 レポジトリ名 プロファイル名
このようにイメージがpushされました。
3. Lambdaでの実行
pushしたコンテナイメージを、Lambdaでコンテナとして実行してみました。Lambdaは別のCDKのプロジェクトを作ってデプロイしました。
mkdir cdk_lambda cd cdk_lambda cdk init sample-app --language typescript
lib/cdk_lambda-stack.ts
は以下のようにしました。
import * as cdk from 'aws-cdk-lib'; import * as ecr from "aws-cdk-lib/aws-ecr"; import * as lambda from "aws-cdk-lib/aws-lambda"; import { Construct } from 'constructs'; export class CdkLambdaStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // ECRリポジトリARNを指定して、Lambda関数用カスタムコンテナイメージのリポジトリを取得する。 const lambdaEcrRepository = ecr.Repository.fromRepositoryArn( this, id, this.node.tryGetContext("ecrRepoArn") ); // Python関数 const sampleLambda = new lambda.Function(this, "cm-nayuts-sample-container-lambda", { code: lambda.Code.fromEcrImage(lambdaEcrRepository, { cmd: ["app.handler"], tag: "latest", }), functionName: "cm-nayuts-sample-container-lambda", runtime: lambda.Runtime.FROM_IMAGE, handler: lambda.Handler.FROM_IMAGE, timeout: cdk.Duration.seconds(3) }); } }
ECRリポジトリのARNは、cdk.json
のcontext
に、ecrRepoArn
のバリューとして設定しておきました。
そして、以下のコマンドでデプロイしました。
cdk deploy --profile プロファイル名
以下のように関数が作成されました。
テストから実行してみると、app.py
が実行されていることが分かりました。
後片付け
それぞれのCDKプロジェクトで、スタックを削除しておきました。
cdk destroy --profile プロファイル名
ECRのレポジトリもリポジトリタグの効果でスタックを削除すると綺麗に削除されました。検証時は便利ですが、運用時は間違って消される可能性もありそうなのでそこは注意ですね。
最後に
Rancher Desktopをインストールしたローカル環境から、Amazon ECRにコンテナイメージを準備してAWS Lambdaで実行してみたので、試した内容をまとめてみました。
簡単な例ではありましたが、Lambdaで実行するコンテナを変えればさまざまなことが実現できますし、ECRにpushする方法まで分かったのでSageMakerのProcessing Jobを使うときなどにも応用ができますね。